<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>KDJ指标计算器</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            max-width: 1000px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
            color: #333;
        }
        h1 {
            color: #2c3e50;
            text-align: center;
            margin-bottom: 30px;
        }
        .container {
            display: flex;
            gap: 20px;
            flex-wrap: wrap;
        }
        .input-section {
            flex: 1;
            min-width: 300px;
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .chart-section {
            flex: 2;
            min-width: 600px;
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        textarea {
            width: 100%;
            height: 150px;
            margin-bottom: 15px;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-family: monospace;
        }
        button {
            background-color: #3498db;
            color: white;
            border: none;
            padding: 10px 15px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            margin-right: 10px;
        }
        button:hover {
            background-color: #2980b9;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
        }
        th, td {
            padding: 8px 12px;
            text-align: center;
            border: 1px solid #ddd;
        }
        th {
            background-color: #f2f2f2;
        }
        tr:hover {
            background-color: #f9f9f9;
        }
        .chart-container {
            height: 400px;
            position: relative;
        }
        .legend {
            margin-top: 20px;
            display: flex;
            gap: 15px;
        }
        .legend-item {
            display: flex;
            align-items: center;
            gap: 5px;
        }
        .color-box {
            width: 15px;
            height: 15px;
            border-radius: 3px;
        }
        .error {
            color: #e74c3c;
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <h1>KDJ指标计算器</h1>
    
    <div class="container">
        <div class="input-section">
            <h2>输入股票数据</h2>
            <p>请输入CSV格式数据（日期,最高价,最低价,收盘价），每行一条记录：</p>
            <textarea id="priceData">日期,最高价,最低价,收盘价
2023-01-03,105.5,103.2,104.8
2023-01-04,107.8,104.5,106.3
2023-01-05,108.9,106.1,107.5
2023-01-06,109.7,107.2,108.9
2023-01-09,111.2,108.6,110.5
2023-01-10,112.8,110.1,111.9
2023-01-11,113.5,110.8,112.3
2023-01-12,114.2,112.5,113.8
2023-01-13,115.9,113.6,114.7
2023-01-16,116.5,114.2,115.8
2023-01-17,117.2,115.3,116.5
2023-01-18,118.0,116.2,117.3
2023-01-19,117.8,115.9,116.7
2023-01-20,118.5,116.8,117.9</textarea>
            <div>
                <button id="calculateBtn">计算KDJ</button>
                <button id="exampleBtn">使用示例数据</button>
            </div>
            <div id="errorMsg" class="error"></div>
        </div>
        
        <div class="chart-section">
            <h2>KDJ指标图表</h2>
            <div class="chart-container" id="chart">
                <canvas id="chart-canvas"></canvas>
            </div>
            <div class="legend">
                <div class="legend-item">
                    <div class="color-box" style="background-color: #3498db;"></div>
                    <span>K线</span>
                </div>
                <div class="legend-item">
                    <div class="color-box" style="background-color: #e74c3c;"></div>
                    <span>D线</span>
                </div>
                <div class="legend-item">
                    <div class="color-box" style="background-color: #2ecc71;"></div>
                    <span>J线</span>
                </div>
            </div>
            
            <h2>计算结果</h2>
            <div id="results">
                <table>
                    <thead>
                        <tr>
                            <th>日期</th>
                            <th>收盘价</th>
                            <th>K值</th>
                            <th>D值</th>
                            <th>J值</th>
                        </tr>
                    </thead>
                    <tbody id="resultBody">
                    </tbody>
                </table>
            </div>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script>
        // 全局变量
        let chart = null;
        let priceData = [];
        
        // 初始化页面
        document.addEventListener('DOMContentLoaded', function() {
            document.getElementById('calculateBtn').addEventListener('click', calculateKDJ);
            document.getElementById('exampleBtn').addEventListener('click', loadExampleData);
        });
        
        // 加载示例数据
        function loadExampleData() {
            document.getElementById('priceData').value = `日期,最高价,最低价,收盘价
2023-01-03,105.5,103.2,104.8
2023-01-04,107.8,104.5,106.3
2023-01-05,108.9,106.1,107.5
2023-01-06,109.7,107.2,108.9
2023-01-09,111.2,108.6,110.5
2023-01-10,112.8,110.1,111.9
2023-01-11,113.5,110.8,112.3
2023-01-12,114.2,112.5,113.8
2023-01-13,115.9,113.6,114.7
2023-01-16,116.5,114.2,115.8
2023-01-17,117.2,115.3,116.5
2023-01-18,118.0,116.2,117.3
2023-01-19,117.8,115.9,116.7
2023-01-20,118.5,116.8,117.9
2023-01-23,119.2,117.5,118.8
2023-01-24,119.8,118.1,119.3
2023-01-25,120.5,119.0,120.2
2023-01-26,121.2,120.0,120.9
2023-01-27,121.8,120.5,121.4
2023-01-30,122.5,121.2,122.0
2023-01-31,123.0,121.8,122.7`;
            calculateKDJ();
        }
        
        // 计算KDJ指标
        function calculateKDJ() {
            // 清空错误信息
            document.getElementById('errorMsg').textContent = '';
            
            // 获取输入数据
            const inputData = document.getElementById('priceData').value.trim();
            if (!inputData) {
                showError('请输入股票数据');
                return;
            }
            
            // 解析CSV数据
            try {
                const lines = inputData.split('\n');
                priceData = [];
                
                for (let i = 1; i < lines.length; i++) {
                    const line = lines[i].trim();
                    if (!line) continue;
                    
                    const parts = line.split(',');
                    if (parts.length < 4) {
                        showError(`第${i+1}行数据格式不正确，需要4个值（日期,最高价,最低价,收盘价）`);
                        return;
                    }
                    
                    const date = parts[0].trim();
                    const high = parseFloat(parts[1]);
                    const low = parseFloat(parts[2]);
                    const close = parseFloat(parts[3]);
                    
                    if (isNaN(high) || isNaN(low) || isNaN(close)) {
                        showError(`第${i+1}行中的价格必须是数字`);
                        return;
                    }
                    
                    priceData.push({ date, high, low, close });
                }
                
                if (priceData.length < 9) {
                    showError('至少需要9天的数据才能计算KDJ');
                    return;
                }
                
                // 计算KDJ
                const kdjResults = calculateKDJValues(priceData);
                
                // 显示结果
                displayResults(kdjResults);
                
                // 绘制图表
                drawChart(kdjResults);
                
            } catch (error) {
                showError('数据处理错误: ' + error.message);
            }
        }
        
        // 计算KDJ值
        function calculateKDJValues(data) {
            const n = 9; // KDJ周期，通常为9日
            const m1 = 3; // K值平滑周期
            const m2 = 3; // D值平滑周期
            
            const results = [];
            const kValues = [];
            const dValues = [];
            
            for (let i = 0; i < data.length; i++) {
                if (i < n - 1) {
                    // 前8天数据不足，无法计算KDJ
                    results.push({
                        date: data[i].date,
                        close: data[i].close,
                        k: null,
                        d: null,
                        j: null
                    });
                    continue;
                }
                
                // 获取最近n天的最高价和最低价
                let periodHigh = -Infinity;
                let periodLow = Infinity;
                
                for (let j = i - n + 1; j <= i; j++) {
                    if (data[j].high > periodHigh) periodHigh = data[j].high;
                    if (data[j].low < periodLow) periodLow = data[j].low;
                }
                
                // 计算RSV
                const rsv = ((data[i].close - periodLow) / (periodHigh - periodLow)) * 100;
                
                // 计算K值
                let k;
                if (kValues.length === 0) {
                    k = 50; // 初始K值通常设为50
                } else {
                    k = (2/3) * kValues[kValues.length - 1] + (1/3) * rsv;
                }
                kValues.push(k);
                
                // 计算D值
                let d;
                if (dValues.length === 0) {
                    d = 50; // 初始D值通常设为50
                } else {
                    d = (2/3) * dValues[dValues.length - 1] + (1/3) * k;
                }
                dValues.push(d);
                
                // 计算J值
                const j = 3 * k - 2 * d;
                
                results.push({
                    date: data[i].date,
                    close: data[i].close,
                    k: parseFloat(k.toFixed(2)),
                    d: parseFloat(d.toFixed(2)),
                    j: parseFloat(j.toFixed(2))
                });
            }
            
            return results;
        }
        
        // 显示计算结果
        function displayResults(results) {
            const resultBody = document.getElementById('resultBody');
            resultBody.innerHTML = '';
            
            for (const result of results) {
                const row = document.createElement('tr');
                
                row.innerHTML = `
                    <td>${result.date}</td>
                    <td>${result.close}</td>
                    <td>${result.k !== null ? result.k : '-'}</td>
                    <td>${result.d !== null ? result.d : '-'}</td>
                    <td>${result.j !== null ? result.j : '-'}</td>
                `;
                
                resultBody.appendChild(row);
            }
        }
        
        // 绘制KDJ图表
        function drawChart(results) {
            console.log(document.getElementById('chart'))
            const ctx = document.getElementById('chart-canvas').getContext('2d');
            
            // 销毁现有图表（如果存在）
            if (chart) {
                chart.destroy();
            }
            
            // 准备图表数据
            const dates = results.map(r => r.date);
            const kValues = results.map(r => r.k);
            const dValues = results.map(r => r.d);
            const jValues = results.map(r => r.j);
            
            chart = new Chart(ctx, {
                type: 'line',
                data: {
                    labels: dates,
                    datasets: [
                        {
                            label: 'K线',
                            data: kValues,
                            borderColor: '#3498db',
                            backgroundColor: 'rgba(52, 152, 219, 0.1)',
                            borderWidth: 2,
                            pointRadius: 3,
                            fill: false
                        },
                        {
                            label: 'D线',
                            data: dValues,
                            borderColor: '#e74c3c',
                            backgroundColor: 'rgba(231, 76, 60, 0.1)',
                            borderWidth: 2,
                            pointRadius: 3,
                            fill: false
                        },
                        {
                            label: 'J线',
                            data: jValues,
                            borderColor: '#2ecc71',
                            backgroundColor: 'rgba(46, 204, 113, 0.1)',
                            borderWidth: 2,
                            pointRadius: 3,
                            fill: false
                        }
                    ]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    scales: {
                        x: {
                            title: {
                                display: true,
                                text: '日期'
                            },
                            ticks: {
                                maxRotation: 45
                            }
                        },
                        y: {
                            title: {
                                display: true,
                                text: 'KDJ值'
                            },
                            suggestedMin: 0,
                            suggestedMax: 100
                        }
                    },
                    plugins: {
                        tooltip: {
                            mode: 'index',
                            intersect: false
                        },
                        legend: {
                            position: 'top',
                        }
                    }
                }
            });
        }
        
        // 显示错误信息
        function showError(message) {
            document.getElementById('errorMsg').textContent = message;
        }
    </script>
</body>
</html>